#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <sys/stat.h>
#include <fcntl.h>

//打印格式
#define PRINTFORMAT "[%s@%s:%s]$ " 

//存放用户输入的命令行
#define COMLINESIZE 1024
char GetComLine[COMLINESIZE];

//命令行参数表
#define ARGVSIZE 1024
char *g_argv[ARGVSIZE];
int g_argc = 0;

//环境变量表
#define ENVSIZE 1024
char *Env[ENVSIZE];

//退出信息
int status = 0;

//重定向
#define NON_REDIR 0
#define IN_REDIR 1
#define OUT_REDIR 2
#define SUP_REDIR 3
std::string filename;
int redir = 0;


//初始化环境变量
void InitEnv()
{
	memset(Env, 0, sizeof(Env));
	extern char **environ;	

	char *env = *environ;
	for(int i = 0; *(environ + i); ++i)
	{
		//Env[i] = *(environ + 1);
		char *tmp = (char*)malloc(strlen(*(environ + i)) + 1);
		strcmp(tmp, *(environ + i));
		Env[i] = tmp;
		putenv(Env[i]);
	}
	char *put = "HAHA=112233";
	putenv(put);
}

//获取用户名
const char *GetUsrName()
{
	const char *UsrName = getenv("USER");
	return UsrName ? UsrName : "";
}

//获取主机名
const char *GetHostName()
{
	//const char *HostName = getenv("HOSTNAME");
	//return HostName ? HostName : "";

	const char *HostName = "hcss-ecs-40db";
	return HostName;
}

//获取当前路径
const char *GetPwd()
{
	//const char *Pwd = getenv("PWD");
	//return Pwd ? Pwd : "";

	char buffer[1024];
	const char* Pwd = getcwd(buffer, sizeof(buffer));
	return Pwd ? Pwd : "";
	//if(getcwd(buffer, sizeof(buffer)));
	//if({

	//if(	return buffer;
	//if(}
	//if(return "";

}

//获取家目录
const char* GetHome()
{
	const char* Home = getenv("HOME");
	return Home ? Home : "";
}
//打印命令行提示符
void PrintComPrompt()
{
	printf(PRINTFORMAT, GetUsrName(), GetHostName(), GetPwd());
}

//获取用户输入
bool GetInput()
{
	if(!fgets(GetComLine, sizeof(GetComLine), stdin))
		 return false;

	GetComLine[strlen(GetComLine) - 1] = '\0';
	if(strlen(GetComLine) == 0)
		return false;
	return true;
}

//处理重定向
void Redirect()
{
	redir = NON_REDIR;
	filename.clear();

	int n = strlen(GetComLine) - 1;
	int left = 0, right = n;
	while(left < right)
	{
		if(GetComLine[right] == '>')
		{
			if(GetComLine[right - 1] == '>')
			{
				GetComLine[right - 1] = 0;
				redir = SUP_REDIR;
			}
			else
				redir = OUT_REDIR;

			GetComLine[right++] = 0;
			while(right < n && GetComLine[right] == ' ')
				++right;
			filename = &GetComLine[right];
			break;
		}
		else if(GetComLine[right] == '<')
		{
			GetComLine[right++] = 0;
			while(right < n && GetComLine[right] == ' ')
				++right;
			filename = &GetComLine[right];
			redir = IN_REDIR;
			break;
		}
		--right;
	}
}


//切割字符串
bool GetArgv()
{
	memset(g_argv, 0, sizeof(g_argv));
	g_argc = 0;
	for(char *ret = strtok(GetComLine, " "); ret; ret = strtok(NULL, " "))
	{
		g_argv[g_argc++] = ret;
	}
	g_argv[g_argc] = NULL;
	return g_argc ? true : false;
}

//进程替换
void Replace()
{
	pid_t id = fork();
	if(id == 0)
	{
		if(redir == OUT_REDIR)
		{
			int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
			if(fd < 0)	exit(1);
			dup2(fd, 1);
		}
		else if(redir == SUP_REDIR)
		{
			int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
			if(fd < 0)	exit(1);
			dup2(fd, 1);
		}
		else if(redir == IN_REDIR)
		{
			int fd = open(filename.c_str(), O_RDONLY);
			if(fd < 0)	exit(1);
			dup2(fd, 0);
		}
		execvp(g_argv[0], g_argv);
		exit(0);
	}
	status = 0;
	waitpid(id, &status, 0);
	
}

//执行cd
void ExecuteCd()
{
	char push[1024] = "PWD=\0";
	if(g_argc == 1 || (g_argc == 2 && strcmp(g_argv[1], "~")) == 0)
	{
		chdir(GetHome());	
		strcat(push, GetHome());
	}
	else
	{
		chdir(g_argv[1]);
		strcat(push, g_argv[1]);
	}
	putenv(push);
}

//执行echo
void ExecuteEcho()
{
	if(strcmp(g_argv[1], "$?") == 0)
	{
		printf("%d\n", WEXITSTATUS(status));
		status = 0;
	}	
	else if(g_argv[1][0] == '$')
	{
		const char* env = g_argv[1] + 1;
		//std::string s = env + 1;
		printf("%s\n", getenv(env));
	}
	else
		printf("%s\n", g_argv[1]);
}

//判断是否是内键命令
bool judgment()
{
	if(strcmp(g_argv[0], "cd") == 0)
	{
		//执行cd命令
		ExecuteCd();
		return true;
	}
	else if(strcmp(g_argv[0], "echo") == 0)
	{
		//执行echo命令
		ExecuteEcho();
		return true;
	}
	return false;
}


int main()
{
	//初始化环境变量表
	InitEnv();
	while(1){
		//打印命令行提示符
		PrintComPrompt();
		
		//获取用户输入
		if(!GetInput())
			continue;
		//printf("%s\n", GetComLine);
		
		//判断是否需要重定向并完成切割
		Redirect();

		//切割字符串
		if(!GetArgv())
			continue;
		//for(int i = 0; i < g_argc; ++i)
		//{
		//	printf("%s\n", g_argv[i]);
		//}
		
		//判断是否是内键命令
		if(judgment())
			continue;

		//进程替换
		Replace();
	}

	//测试
	//const char *HostName = getenv("HOSTNAME");
	//conif(HostName)
	//con	printf("%s\n", HostName);
	//conelse
	//con	printf("BUG\n");

	return 0;
}
